#ifndef A8_UDPCLIENT_H
#define A8_UDPCLIENT_H

#include <a8/netbase.hpp>

namespace a8
{
	class UdpClient
	{
		const static unsigned short MAX_RECV_BUFFERSIZE = 1024 * 8;

		struct SendQueueNode
		{
			char* mBuff;
			unsigned int mBuffLen;
			unsigned int mSentBytes;
			SendQueueNode *mNext;
		};
	public:
		std::string mRemoteAddress;
		int mRemotePort;

		UdpClient():mRemotePort(0), mSocket(INVALID_SOCKET), mTopNode(NULL), mBotNode(NULL),
					mSenderThread(NULL)
		{
		}

		virtual ~UdpClient()
		{
			Close();
		}

		void Open()
		{
			if(!IsActive()){
				Close();
				mSocket = socket(AF_INET, SOCK_DGRAM, 0);
				if(INVALID_SOCKET == mSocket){
					//					OnError(this, errno);
					return;
				}
				bzero(&mAddr, sizeof(mAddr));
				mAddr.sin_family = AF_INET;
				mAddr.sin_port = htons(mRemotePort);
				mAddr.sin_addr.s_addr = inet_addr(mRemoteAddress.c_str());
				mSenderThread = new a8::Runnable();
				mSenderThread->OnExecute += std::make_pair(this, &UdpClient::SenderThreadProc);
				mSenderThread->Start();
			}
		}

		void Close()
		{
			if(IsActive()){
				closesocket(mSocket);				
				mSocket = INVALID_SOCKET;
				if(mSenderThread){
					delete mSenderThread;
					mSenderThread = NULL;
				}
			}
		}

		bool IsActive()
		{
			return mSocket != INVALID_SOCKET;
		}

		void SendBuff(const char* buff, unsigned int bufflen)
		{	
			if (bufflen > 0){
				SendQueueNode* p = (SendQueueNode*)malloc(sizeof(SendQueueNode));
				memset(p, 0, sizeof(SendQueueNode));
				p->mBuff = (char*)malloc(bufflen);
				memmove(p->mBuff, buff, bufflen);
				p->mBuffLen = bufflen;			
				mSendBufferMutex.Lock();
				if (mBotNode){
					mBotNode->mNext = p;
					mBotNode = p;
				}else{
					mTopNode = p;
					mBotNode = p;
				}
				mSendBufferMutex.UnLock();
			}
		}

	private:
		volatile SOCKET mSocket;
		a8::sync::Cond mSendBufferCond;
		a8::sync::Mutex mSendBufferMutex;
		SendQueueNode *mTopNode;
		SendQueueNode *mBotNode;
		a8::Runnable *mSenderThread;
		struct sockaddr_in mAddr;

		void SenderThreadProc(a8::Runnable* thread)
		{
			SendQueueNode *worknode = NULL, *delnode = NULL;			
			while(!thread->IsShutdown()){
				if(!worknode && mTopNode){
					mSendBufferMutex.Lock();
					worknode = mTopNode;
					mTopNode = NULL;
					mBotNode = NULL;
					mSendBufferMutex.UnLock();
				}

				while(worknode && !thread->IsShutdown()){
					::sendto(mSocket, worknode->mBuff, worknode->mBuffLen, 0, (struct sockaddr*)&mAddr, sizeof(mAddr));
					delnode = worknode;
					worknode = worknode->mNext;
					free(delnode->mBuff);
					free(delnode);
				}
				mSendBufferCond.Wait(10);
			}
			while(worknode){
				delnode = worknode;
				worknode = worknode->mNext;
				free(delnode->mBuff);
				free(delnode);
			}
		}

	};
}

#endif
